-- cover # React
Redux -- who **David Barral** david@trabesoluciones.com @davidbarral **Román Coedo** roman@trabesoluciones.com @rcoedo **Asís García** asis@trabsoluciones.com @asischao -- slidepack ## Esta charla está hecha con # SlidePack ## _"Simple slides generator & viewer"_ [trabe.github.io/slide-pack](http://trabe.github.io/slide-pack) -- disclaimer ## En capítulos anteriores # ES6
Babel
React -- # OBJETIVOS ## Repaso a React ## La M del MVC ## Introducir Redux -- ![](./assets/react.png) https://facebook.github.io/react/ -- # React ## Filosofía * Librería de UI * UI = f(estado) * Virtual DOM * Uniderectional data flow -- # Componentes ```javascript import React from "react"; import {render} from "react-dom"; const HelloWorld = () =>
Hello world!
; render(
, document.getElementById("app") ); ``` -- # What the <Fuck/> !? ## JSX (syntactic sugar) -- # Props ```javascript import React from "react"; import {render} from "react-dom"; const HelloWorld = (props) =>
Hello {props.name}!
; render(
, document.getElementById("app") ); ``` -- # Funciones vs clases ```javascript import React from "react"; import {render} from "react-dom"; class HelloWorld extends React.Component { render() { return
Hello {this.props.name}!
; } } render(
, document.getElementById("app") ); ``` -- # [Estado y eventos](http://jsbin.com/dixitubaco/embed?js,output) ```javascript class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { casual: props.casual }; this.toggleStyle = this.toggleStyle.bind(this); } toggleStyle() { this.setState({ casual: !this.state.casual }); } render() { const greeting = this.state.casual ? "Wasap" : "Hello"; return
{greeting} {this.props.name}!
; } } render(
, document.getElementById("app") ); ``` -- # Children ```javascript const Emphasis = ({children}) => { return (
WOOOOOOOOWWWWW!!! {children} WWAAAAAAAWWWAAAAAAAAAA!!!
); } render(
, document.getElementById("app") ); ``` -- # Otros temas * Ciclo de vida * JSX quirks * propTypes * Contexto -- # Inmutabilidad ```javascript function addToArray(array, element) { return [...array, element]; } function removeFromArray(array, position) { return [...array.slice(0, position), ...array.slice(position + 1)]; } const array1 = [1,2,3,4]; const array2 = addToArray(array1, 5); // [1,2,3,4,5] const array3 = removeFromArray(array2, 4); // [1,2,3,4] array1 === array3 // false ``` -- # Inmutabilidad ```javascript const john = { name: "John", age: 27, } function becomeOlder(person) { return { ...person, age: person.age + 1 }; } const olderJohn = becomeOlder(john); john.age // 27 olderJohn.age // 28 john === olderJohn // false ``` -- # Funciones puras e impuras ```javascript // Pure function const double = (x) => { return 2 * x; } // Impure function const double = (x) => { const result = 2 * x; storeDoubledNumbers(x, result) // side effect return result; } const storeDoubledNumbers(number, double) { fetch("/store_doubled_numbers.json", { method: "POST", body: JSON.stringify({ number, double }) ); } ``` -- # La M del MVC * Unidirectional data flow * Flux * Redux -- # Code cartoons by Lin Clark https://code-cartoons.com * Flux https://code-cartoons.com/a-cartoon-guide-to-flux-6157355ab207#.h6ouot9mo * Redux https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6#.p0w2c76yg * Hot reload and time travel debugging https://code-cartoons.com/hot-reloading-and-time-travel-debugging-what-are-they-3c8ed2812f35#.w2wvn5e9a -- image # Unidirectional data flow ![Unidirectional data flow](./assets/unidirectional-data-flow.png) -- image # Flux ![Flux](./assets/flux.png) https://facebook.github.io/flux/ -- # Estado compartido ![Estado compartido](./assets/state1.png) -- # Estado compartido ![Estado compartido](./assets/state2.png) -- # Estado compartido ![Estado compartido](./assets/state3.png) -- # Estado compartido ![Estado compartido](./assets/state4.png) -- # Estado compartido ![Estado compartido](./assets/state5.png) -- # ¿Por qué Redux? * Gestionar el cambio de estado de manera centralizada * Hot reloading sin perder el estado * Time-travel development -- quoting # Redux: principios >**Single source of truth** > >The state of your whole application is stored in an object tree within a single store. > >** State is read-only** > >The only way to mutate the state is to emit an action, an object describing what happened. > >**Changes are made with pure functions** > >To specify how the state tree is transformed by actions, you write pure reducers. -- # Redux: Flux alike ![Unidirectional data flow](./assets/redux-flow.png) -- # Redux * Objeto estado único e inmutable * Reducer: `f(state, action) => state'` * `store = state + reducers` * `store.dispatch(action)` -- # Actions y Action Creators ```javascript const ADD_TODO = "ADD_TODO"; // { // type: ADD_TODO, // text: "Follow the leader, leader, leader" // } function addTodo(text) { return { type: ADD_TODO, text }; } ``` -- # Reducers ```javascript const initialState = []; export default function todosReducer(state, action) { if (typeof state === "undefined") { return initialState; } switch(action.type) { case ADD_TODO: return [...state, action.text ]; default: return state; } } ``` -- # [Store](http://jsbin.com/vabivaluxu/1/edit?js,console) ```javascript import { createStore } from "redux"; import todosReducer from "./reducers"; const store = createStore(todosReducer); const unsubscribe = store.subscribe(() => { console.log(store.getState()); }); store.dispatch(addTodo("Explain the code")); store.dispatch(addTodo("Explain the code oonce more")); ``` -- # Actions ```javascript const ADD_TODO = "ADD_TODO"; const REMOVE_TODO = "REMOVE_TODO"; const ADD_REMINDER = "ADD_REMINDER"; // { // type: ADD_TODO, // text: "Follow the leader, leader, leader" // } // // { // type: REMOVE_TODO, // position: 1 // } // // { // type: ADD_REMINDER, // text: "Do not forget to follow the leader" // } ``` -- # Action creators ```javascript function addTodo(text) { return { type: ADD_TODO, text }; } function removeTodo(position) { return { type: REMOVE_TODO, position }; } function addReminder(text) { return { type: ADD_REMINDER, text }; } ``` -- # Reducers ```javascript import { combineReducers } from "redux"; const initialState = { todos: [], reminders: [] } function todosReducer(todos = initialState.todos, action) { switch(action.type) { case ADD_TODO: return [...todos, action.text]; case REMOVE_TODO: return [ ...todos.slice(0, action.position), ...todos.slice(action.position + 1) ]; default: return todos; } } ``` -- # Reducers ```javascript function remidersReducer(reminders = initialState.reminders, action) { switch(action.type) { case ADD_REMINDER: return [...reminders, action.text ]; default: return reminders; } } export default combineReducers({ todos: todosReducer, reminders: remidersReducer }); ``` -- # [Store](http://jsbin.com/lixuqohabe/edit?js,console) ```javascript import { createStore } from "redux"; import appReducer from "./reducers"; const store = createStore(apReducer); cont unsubscribe = store.subscribe(() => { console.log(store.getState()); }); store.dispatch(addTodo("Explain the code")); store.dispatch(addTodo("Explain the code oonce more")); store.dispatch(removeTodo(1)); store.dispatch(addReminder("Don't forget the code")); ``` -- # React y Redux * Componentes de vista * Sólo renderizan UI * API vía `props` * Componentes conectados * Agregan componentes de vista * Acceden a la `store` a través del contexto * Acceden al estado de la `store` * Pasan `props` (estado) y _callbacks_ a sus `children` -- # Componentes de vista ```javascript import React from "react"; const Todo = ({text, onClick}) =>
{text}
; const Todos = ({todos, onTodoClick}) => { const todoItems = todos.map((todo, i) =>
{ event.preventDefault(); onTodoClick(i); }}/> ); return
{todoItems}
; } ``` -- # Componentes contenedores ```javascript export default connect( state => ({ todos: state.todos }), dispatch => ({ onTodoClick: position => { dispatch(removeTodo(position)) } }) )(Todos); ``` -- # [Redux: provider](http://jsbin.com/piqifaropi/1/edit?js,output) ```javascript import React from "react"; import { render } from "react-dom"; import { Provider } from "react-redux"; import { createStore } from "redux"; import appReducer from "./reducers"; import Todos from "./todos"; const store = createStore(appReducer); render(
, document.getElementById("root") ); ``` -- # Efectos laterales * redux-thunk * redux-saga * redux-loop -- demo # LIVE DEMO https://github.com/trabe-teaching/redux-y-reactjs-demo -- image # ¿Preguntas? ![](./assets/questions.jpg) -- # ¡Eso es todo! ![](./assets/nice.gif)